import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;



public class ClassesToSplot {

	public int contadorGlobal=5;
	private int cantidadEspacios=1;
	private Map<String, String> mapConfigs = new HashMap<String, String>();
	private static String constraintGlobal="";

	public String classesToSplot(Menu principal) throws Excepcion{
		String splot = ":r "+ principal.getWordQuote()+ "(_r) " + '\n';
		return splot+recurConfigs(principal.getConfigs(), "_r")+recurChoice(principal.getChoices(), "_r")+ recurMenu(principal.getMenus(), "_r")+recurMenuConf(principal.getMenuconfigs(), "_r")+constraintGlobal;
		
		
	}
	
	private String recurConfigs(List<String> configs, String nivel) throws Excepcion {
		String niveles;
		String cadena="";
		boolean bandera= false;
		boolean bandera_constraint=false;
		if(!configs.isEmpty()){
			
			for (String config : configs) {
				
				niveles= nivel+"_"+ ++contadorGlobal;
				Config objetoConfig = Main.mapConfigs.get(config);
			
				if(objetoConfig.getDepends()!=null){
					if(objetoConfig.getDepends().getType()==1){
						niveles= mapConfigs.get(objetoConfig.getDepends().getExpr())+"_"+ ++contadorGlobal; 
						bandera =true;
						//System.out.println("bandera true"+ config);
					}
					else{
						bandera_constraint=true;
						
					}
						
				}
				if(!bandera){
					for(int i=0; i<cantidadEspacios; i++){
						cadena= cadena+'\t';
					}
				}
				
				if( (objetoConfig.getType().getType().equals("bool") || objetoConfig.getType().getType().equals("tristate")) && objetoConfig.getPrompt()!=null){
					if(bandera){
						String s =mapConfigs.get(objetoConfig.getDepends().getExpr());
						String[] cadena_div = cadena.split(s);
						cadena=cadena_div[0]+mapConfigs.get(objetoConfig.getDepends().getExpr())+ ") "+'\n';
						for(int i=0; i<cantidadEspacios; i++){
							cadena= cadena+'\t';
						}
						cadena+='\t'+":o " + config+"("+niveles+") " +cadena_div[1].charAt(0)+cadena_div[1].substring(1, cadena_div[1].length());
						
					}
					
					else cadena= cadena + ":o " + config+"("+niveles+") "+'\n';
				}
				else{
					if (bandera){
						String[] cadena_div = cadena.split(mapConfigs.get(objetoConfig.getDepends().getExpr())); 
						cadena=cadena_div[0]+mapConfigs.get(objetoConfig.getDepends().getExpr())+ ") "+ '\n';
						for(int i=0; i<cantidadEspacios; i++){
							cadena= cadena+'\t';
						}
						cadena+='\t'+ ":m " + config+"("+niveles+") "  +cadena_div[1].substring(1, cadena_div[1].length());
					}
					else cadena= cadena + ":m " + config+"("+niveles+") "+'\n';
					
				}
				bandera=false;
				mapConfigs.put(objetoConfig.getSymbol(), niveles);
				if(bandera_constraint){
					constraint(config,objetoConfig.getDepends());
				}
				bandera_constraint=false;
				
				if(!objetoConfig.getSelect().isEmpty()){
					for (Select select : objetoConfig.getSelect()) {
						constraintGlobal+='\n'+ "~"+objetoConfig.getSymbol()+ " or " + select.getSymbol();
						
					}
				}
			}
		}
		return cadena;
	}

	private String recurChoice(List<Choice> choices, String nivel) throws Excepcion {
		String cadena="";
		
		if(!choices.isEmpty()){
		
			for (Choice choice : choices) {
				//System.out.println(choice.getSymbol());
				for(int i=0; i<cantidadEspacios; i++){
					cadena= cadena+'\t';
				}
				String nivelChoice= nivel+"_"+ ++contadorGlobal;	
				
				if (choice.getPrompt()!=null){
					cadena+=":m "+choice.getPrompt().getPrompt()+"("+nivelChoice+") "+ '\n';
				}
				else{
					cadena+=":m choice"+"("+nivelChoice+") "+ '\n';
				}
				cantidadEspacios++;
				for(int i=0; i<cantidadEspacios; i++){
					cadena= cadena +'\t';
				}
				String nivgrupo= nivelChoice+"_"+ ++contadorGlobal;
				
				if(choice.isOptional() &&choice.getType()!= null && choice.getType().getValue()!=null && choice.getType().getValue().equals("n")){
					if(choice.getType().getType().equals("bool")){
						cadena= cadena+":g ("+ nivgrupo + ") [0,1] "+'\n';//es xor de 0..1
					}
					else if(choice.getType().getType().equals("tristate")){
						cadena= cadena+ ":g ("+ nivgrupo + ") [0,*] "+'\n';//es or 0..*
					}
				}
				else if (choice.getType()!= null){
					if(choice.getType().getType().equals("bool")){
						cadena= cadena+":g ("+ nivgrupo + ") [1,1] "+'\n';//es xor de 1..1
					}
					else if(choice.getType().getType().equals("tristate")){
						cadena= cadena+ ":g ("+ nivgrupo + ") [1,*] "+'\n';//es or 1..*
					}
				}
				else {
					throw new Excepcion("La choice no tiene tipo asignado");
				}
				
							
				if(!choice.getConfig().isEmpty()){
					cantidadEspacios++;
					for (String config : choice.getConfig()) {
						
						String aux= nivgrupo+"_"+ ++contadorGlobal;
						for(int i=0; i<cantidadEspacios; i++){
							cadena= cadena +'\t';
						}
						cadena= cadena + ": "+config + "(" + aux +") "+'\n';
					}
					cantidadEspacios--;
				}
				if(!choice.getChoice().isEmpty()){
					cantidadEspacios++;
					cadena+= recurChoice(choice.getChoice(), nivgrupo);
					cantidadEspacios--;
				}
			}
			cantidadEspacios--;
		}
		
		return cadena;
	}

	private String recurMenu(List<Menu> menus, String nivel) throws Excepcion {
			String cadena="";
			
			
			if(!menus.isEmpty()){
				for (Menu menu : menus) {
					for(int i=0; i<cantidadEspacios; i++){
						cadena= cadena +'\t';
					}
					String nivelMenu= nivel+"_"+ ++contadorGlobal;
					cantidadEspacios++;
					cadena= cadena + ":m " + menu.getWordQuote() + "(" + nivelMenu +") " +'\n'+ recurConfigs(menu.getConfigs(), nivelMenu)+ recurChoice(menu.getChoices(), nivelMenu)+ recurMenu(menu.getMenus(), nivelMenu)+recurMenuConf(menu.getMenuconfigs(), nivelMenu);
					cantidadEspacios--;
				}
			}
			
			
			
			
			return cadena;
		}
	private String recurMenuConf(List<String> menuconfigs, String nivel) {
		String cadena="";
		
		if(!menuconfigs.isEmpty()){
			
			for (String string : menuconfigs) {
				for(int i=0; i<cantidadEspacios; i++){
					cadena= cadena +'\t';
				}
				String nivelMenuConfig= nivel+"_"+ ++contadorGlobal;
				
				MenuConfig menuConf = Main.mapMenuConfig.get(string);
				
				if(menuConf.getPrompt()!=null){
					cadena= cadena + ":o " + string+"("+nivelMenuConfig+") "+'\n';
				}
				else{
					cadena= cadena + ":m " + string+"("+nivelMenuConfig+") "+'\n';
				}
				
				if()
			}
		}
		
		return cadena;
	}
	
	
	public void constraint (String symbol, DependsOn dependsOn) throws Excepcion{
		String constraint="";
		String[] symbolsExpr;
		String exprSinParentesis;
		String agregarAlFinal="";
		
		if(symbol.startsWith("!")){
			symbol= "~"+symbol.replace("!", "");
		}
		
		if(dependsOn.getType()==2){
			exprSinParentesis= dependsOn.getExpr().replace("(", "");
			exprSinParentesis=exprSinParentesis.replace(")", "");
			exprSinParentesis=exprSinParentesis.replace(" ", "");
			symbolsExpr= exprSinParentesis.split("\\|\\|");
			constraint = "~"+mapConfigs.get(symbol);
			for(int i=0; i<symbolsExpr.length ; i++ ){
				if(!symbolsExpr[i].startsWith("!")){
					constraint+= " or "+ mapConfigs.get(symbolsExpr[i]);
				}
				else{
					//String aux=symbolsExpr[i].replace("!", "");
					constraint+= " or ~"+mapConfigs.get(symbolsExpr[i].replace("!", "")) ;
				}
			}
			constraint+='\n';
			
			for(int i=0; i<symbolsExpr.length ; i++ ){
				if(!symbolsExpr[i].startsWith("!")){
					constraint+= mapConfigs.get(symbol) + " or ~" + mapConfigs.get(symbolsExpr[i])+'\n';
				}
				else{
					//String aux=symbolsExpr[i].replace("!", "");
					constraint+= mapConfigs.get(symbol) + " or " + mapConfigs.get(symbolsExpr[i].replace("!", ""))+'\n';
				}
				
			}
			
		}
		
		else if(dependsOn.getType()==3){
			exprSinParentesis= dependsOn.getExpr().replace("(", "");
			exprSinParentesis=exprSinParentesis.replace(")", "");
			exprSinParentesis=exprSinParentesis.replace(" ", "");
			symbolsExpr= exprSinParentesis.split("\\&\\&");
			
			constraint = mapConfigs.get(symbol);
			for(int i=0; i<symbolsExpr.length ; i++ ){
				if(!symbolsExpr[i].startsWith("!")){
					constraint+= " or ~"+ mapConfigs.get(symbolsExpr[i]);
				}
				else{
					//String aux=symbolsExpr[i].replace("!", "");
					constraint+= " or "+mapConfigs.get(symbolsExpr[i].replace("!", "")) ;
				}
			}
			constraint+='\n';
			
			for(int i=0; i<symbolsExpr.length ; i++ ){
				
				if(!symbolsExpr[i].startsWith("!")){
					constraint+= "~"+mapConfigs.get(symbol) + " or " + mapConfigs.get(symbolsExpr[i])+'\n';
				}
				else{
					//String aux=symbolsExpr[i].replace("!", "");
					constraint+= "~"+mapConfigs.get(symbol) + " or ~" + mapConfigs.get(symbolsExpr[i].replace("!", ""))+'\n';
				}
			}
			
		}
		else if(dependsOn.getType()==4){
			List<String> exprFinales = new ArrayList<>();
			exprFinales.add("");
			
			
			String expresion = distributiva(dependsOn.getExpr());
			symbolsExpr= expresion.split(" \\|\\| ");
			
			
			for(int i=0; i<symbolsExpr.length ; i++ ){
				
				if(symbolsExpr[i].contains("&&")){
					exprFinales=agregarExpr(exprFinales, symbolsExpr[i].split(" \\&\\& "));
					agregarAlFinal+= mapConfigs.get(symbol) + " or " +pasarDeAndAOr(symbolsExpr[i])+'\n';
				}
				else{
					exprFinales=agregarExpr(exprFinales, symbolsExpr[i]);
					
					if(!symbolsExpr[i].startsWith("!")){
						agregarAlFinal+= mapConfigs.get(symbol) + " or ~"+ mapConfigs.get(symbolsExpr[i])+'\n';
					}
					else{
						//String aux=symbolsExpr[i].replace("!", "");
						agregarAlFinal+= mapConfigs.get(symbol)+ " or "+mapConfigs.get(symbolsExpr[i].replace("!", "")) +'\n';
					}
				}
			}
			
			for(String string : exprFinales){	
				symbolsExpr= string.split(" \\|\\| ");
				
				constraint += "~"+mapConfigs.get(symbol);
				for(int i=0; i<symbolsExpr.length ; i++ ){
					if(!symbolsExpr[i].startsWith("!")){
						constraint+= " or "+ mapConfigs.get(symbolsExpr[i]);
					}
					else{
						//String aux=symbolsExpr[i].replace("!", "");
						constraint+= " or ~"+mapConfigs.get(symbolsExpr[i].replace("!", "")) ;
					}
				}
				constraint+='\n';
			}
		}
		
		else if(dependsOn.getType()==5){
			String auxDepends=dependsOn.getExpr().replace("!", "");
			constraint= "~"+mapConfigs.get(symbol)+" or ~"+ mapConfigs.get(auxDepends)+'\n';
		}
		
		else{
			throw new Excepcion("Error en metodo constraint");
		}
		
		String retorn =constraint+agregarAlFinal;
		constraintGlobal+= '\n'+retorn.replaceAll("~~", "");
		//return retorno;
	}

	private  String distributiva(String expr) {
		String r="";
		for (String s: distribuir(expr)){
			r+=s;
		}
		//System.out.println(r);
		return r;
	}

	private  String pasarDeAndAOr(String string) {
		String retorno="";
		
		String [] expresiones = string.split(" \\&\\& ");
		
		if(!expresiones[0].startsWith("!")){
			retorno+= "~"+ mapConfigs.get(expresiones[0]);
		}
		else{
			//String aux=expresiones[0].replace("!", "");
			retorno+= mapConfigs.get(expresiones[0].replace("!", ""));
		}
		
		for(int i=1; i<expresiones.length; i++){
			if(!expresiones[i].startsWith("!")){
				retorno+= " or ~"+ mapConfigs.get(expresiones[i]);
			}
			else{
				//String aux=expresiones[i].replace("!", "");
				retorno+= " or "+mapConfigs.get(expresiones[i].replace("!", ""));
			}
		}
		
		return retorno;
	}

	private  List<String> agregarExpr(List<String> exprFinales, String string) {
		List<String> retorno= new ArrayList<String>();
		for (String expF : exprFinales) {
			if(!expF.isEmpty())
				retorno.add(expF+" || "+ string );
			else
				retorno.add(string);
		}
		return retorno;
	}

	private  List<String> agregarExpr(List<String> exprFinales, String [] exprAnd) {
		List<String> retorno= new ArrayList<String>();
		
		for (String expF : exprFinales) {
			for(int i=0; i<exprAnd.length; i++){
				retorno.add(expF+" || "+exprAnd[i] );
			}	
		}
		return retorno;
	}
	

	private static List<String> getListaTerminos(String s){
		List<String> arregloTerminos= new ArrayList<String>();
		Stack<Integer> pila=new Stack<Integer>();
		String termino="";
		s=s.replace(" ", "");
	
		
		for(int i=0; i<s.length();i++){
			if(s.substring(i, i+1).equals("(")){
				if(pila.isEmpty() && !termino.equals("")){
						arregloTerminos.add(termino);
						termino="";
					}
				
				pila.push(i);
				termino+=s.substring(i, i+1);
			}
			else if (s.substring(i, i+1).equals(")")){
				pila.pop();
				termino+=s.substring(i, i+1);
				if(pila.isEmpty()){
					arregloTerminos.add(termino);
					termino="";
				}
			}
			else if(!pila.isEmpty()){
				termino+=s.substring(i, i+1);
			}
			else if (s.substring(i, i+1).equals("|")) {
				if(!termino.equals("")){
					arregloTerminos.add(termino);
					termino="";
				}
				arregloTerminos.add(s.substring(i, i+2));
				i++;
			}
			else{
				termino+=s.substring(i, i+1);
			}
		}
		
		
		
		if(!termino.equals("")) arregloTerminos.add(termino);
		
		return arregloTerminos;
	}
	
	private static List<String> distribuir(String s){
		List<String> retornar=new ArrayList<String>();
		List<String> listaTerminos= getListaTerminos(s);
		boolean bandera = true;
		for(int i =0; i< listaTerminos.size(); i++){
			String t = listaTerminos.get(i);
			if(t.contains("(")){
				if(t.substring(1, t.length()).contains("(")){
					retornar.addAll(distribuir(t.substring(1, t.length()-1)));
				}
				else{
					List<String> subLista = getListaTerminos(t.substring(1, t.length()-1));
					if(subLista.size()<2){
						retornar.addAll(subLista);
					}
					else if(i>0){
							retornar.add(subLista.get(0));
							
							for(int j=2; j<subLista.size(); j++){
								if(!(subLista.get(j).equals("||")||subLista.get(j).equals("&&"))){
								retornar.add(subLista.get(1));
								//retornar.add(listaTerminos.get(i-2));
								retornar.add(listaTerminos.get(i-1));
								retornar.add(subLista.get(j));
							}}
						}
					
					else if(i+1<listaTerminos.size()){
							bandera= false;
							retornar.add(subLista.get(0));
							retornar.add(listaTerminos.get(i+1));
							//retornar.add(listaTerminos.get(i+2));
							for(int j=2; j<subLista.size(); j++){
								if(!(subLista.get(j).equals("||")||subLista.get(j).equals("&&"))){
									retornar.add(subLista.get(1));
									retornar.add(subLista.get(j));
									retornar.add(listaTerminos.get(i+1));
									//retornar.add(listaTerminos.get(i+2));
								
								}
							}
							
						
					}
					else retornar.addAll(subLista);
				}
				
			}
			else  if (bandera)retornar.add(t);
		}
		
		return retornar;
	}
	
}
